package com.ea.eadp.nexus.test.data;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.SkipException;

import com.ea.eadp.nexus.test.common.TestUtil;
import com.ea.nucleus.eaid.pid.jaxb.PidsType;
import com.ea.nucleus.eaidpid.integration.client.pids.GetPidIndirectRequest;
import com.ea.nucleus.eaidpid.integration.client.pids.GetPidIndirectService;
import com.fasterxml.jackson.databind.ObjectMapper;

public class WebBasedPINEventLoadService {
    private WebDriver driver = null;
    private ObjectMapper objectMapper = null;
    
    public static void environmentCheck() throws Exception {
        String username = System.getProperty("DOMAIN_USERNAME");
        String password = System.getProperty("DOMAIN_PASSWORD");
        
        if (null == username || null == password) {
            throw new SkipException("skip the case as the domain credential is not available");
        }
    }
    
    public WebBasedPINEventLoadService(WebDriver driver) throws Exception {
        this.driver = driver;
        this.driver.manage().window().maximize();

        driver.get("https://data.ea.com/portal/data/riverlive");
        driver.findElement(By.name("j_username")).sendKeys(System.getProperty("DOMAIN_USERNAME"));
        driver.findElement(By.name("j_password")).sendKeys(System.getProperty("DOMAIN_PASSWORD"));
        driver.findElement(By.name("_spring_security_remember_me")).click();
        driver.findElement(By.name("submit")).click();
        
        this.objectMapper = new ObjectMapper();
    }
    
    void moveToElement(WebElement element) {
        try {
            new Actions(driver).moveToElement(element).perform();
        } catch (Exception e) {
            e.printStackTrace(); // better than nothing...
        }
    }
    
    String emailToPid(String email) throws Exception {
        GetPidIndirectRequest request = new GetPidIndirectRequest();
        request.setEmail(email);
        PidsType pids = GetPidIndirectService.instance().get(request);

        if (pids.getPidUri().isEmpty()) {
            Assert.fail("pid not found for email: " + email);
            return null; // not reachable
        } else {
            return String.valueOf(TestUtil.getPidID(pids.getPidUri().get(0)));
        }
    }

    public List<Map<String, String>> findValidEventByEmail(String email, String eventName) throws Exception {
        return findValidEventByPid(emailToPid(email), eventName);
    }

    public List<Map<String, String>> findValidEventByPid(String pid, String eventName) throws Exception {
        driver.switchTo().window(driver.getWindowHandle());
        driver.switchTo().frame(driver.findElement(By.tagName("iframe")));
        
        try {
            driver.findElement(By.xpath("//label[text()='Select River EndPoint:']"));
        } catch (NoSuchElementException e) {
            driver.findElement(By.xpath("//span[text()='View Events']")).click();
            new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//label[text()='Select River EndPoint:']")));
        }
        
        if (!"PIN".equalsIgnoreCase(driver.findElement(By.id("form:console_label")).getText())) {
            driver.findElement(By.id("form:console_label")).click();
            driver.findElement(By.id("form:console_panel")).findElement(By.xpath("//li[text()='PIN']")).click();
            new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//td[text()='Player ID*']")));
        }
        
        WebElement pidInput = driver.findElement(By.xpath("//td[text()='Player ID*']"))
                .findElement(By.xpath("following-sibling::td")).findElement(By.tagName("input"));
        pidInput.clear();
        pidInput.sendKeys(pid);
        
        WebElement eventNameInput = driver.findElement(By.xpath("//td[text()='Event Name*']"))
                .findElement(By.xpath("following-sibling::td")).findElement(By.tagName("input"));
        eventNameInput.clear();
        eventNameInput.sendKeys(eventName);

        driver.findElement(By.xpath("//span[text()='Submit']")).click();
        new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.xpath(String.format("//input[@value='%s']", pid))));

        List<WebElement> uncheckedFields = driver.findElement(By.id("form:picker_panel"))
                .findElements(By.cssSelector("ul li.ui-selectcheckboxmenu-unchecked"));

        if (!uncheckedFields.isEmpty()) {
            driver.findElement(By.id("form:picker")).click();
            
            for (WebElement field : uncheckedFields) {
                field.click();
            }
    
            driver.findElement(By.id("form:picker")).click();
            
            for (int i = 0; i < 10; i++) {
                try {
                    List<WebElement> fields = driver.findElement(By.id("form:picker_panel")).findElements(By.cssSelector("ul li"));
                    List<WebElement> heads = driver.findElement(By.id("form:overviewDetails_head")).findElements(By.tagName("th"));
                    
                    if (heads.size() == fields.size()) {
                        break;
                    } else {
                        Thread.sleep(100L);
                    }
                } catch (StaleElementReferenceException e) {
                    i--;
                }
            }
        }

        List<WebElement> heads = driver.findElement(By.id("form:overviewDetails_head")).findElements(By.tagName("th"));
        List<String> headNames = new ArrayList<String>(heads.size());

        for (WebElement head : heads) {
            moveToElement(head);
            headNames.add(head.getText());
        }

        List<WebElement> records = driver.findElement(By.id("form:overviewDetails_data")).findElements(By.tagName("tr"));
        List<Map<String, String>> result = new LinkedList<Map<String, String>>();

        for (WebElement record : records) {
            List<WebElement> fields = record.findElements(By.tagName("td"));
            Map<String, String> row = new HashMap<String, String>();

            for (int i = 0; i < fields.size(); i++) {
                moveToElement(fields.get(i));
                row.put(headNames.get(i), fields.get(i).getText());
            }

            if (row.size() == headNames.size()) {
                result.add(row);
            }
        }
        
        return result;
    }
    
    public List<Map<String, String>> findInvalidEventByEmail(String email, String eventName) throws Exception {
        return findInvalidEventByPid(emailToPid(email), eventName);
    }
    
    public List<Map<String,String>> findInvalidEventByPid(String userId, String eventName) throws Exception {
        driver.switchTo().window(driver.getWindowHandle());
        driver.switchTo().frame(driver.findElement(By.tagName("iframe")));
        
        try {
            driver.findElement(By.xpath("//span[text()='Search by Game ID and/or UID:']"));
        } catch (NoSuchElementException e) {
            driver.findElement(By.xpath("//span[text()='Invallid Events']")).click();
            new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//span[text()='Search by Game ID and/or UID:']")));
        }
        
        if (!"PIN".equalsIgnoreCase(driver.findElement(By.id("form:event_type_label")).getText())) {
            driver.findElement(By.id("form:event_type_label")).click();
            driver.findElement(By.id("form:event_type_panel")).findElement(By.xpath("//li[text()='PIN']")).click();
            new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//td[text()='Game ID (project ID, sell ID): ']")));
        }
        
        WebElement gameId = driver.findElement(By.xpath("//td[text()='Game ID (project ID, sell ID): ']"))
                .findElement(By.xpath("following-sibling::td")).findElement(By.tagName("input"));
        gameId.clear();
        gameId.sendKeys("nucleus");
        
        driver.findElement(By.xpath("//span[text()='Submit']")).click();
        new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//input[@value='nucleus']")));
        
        List<WebElement> heads = driver.findElement(By.id("form:tabView_head")).findElements(By.tagName("th"));
        List<String> headNames = new ArrayList<String>(heads.size());

        for (WebElement head : heads) {
            moveToElement(head);
            headNames.add(head.getText());
        }
        
        List<WebElement> records = driver.findElement(By.id("form:tabView_data")).findElements(By.tagName("tr"));
        List<Map<String, String>> result = new LinkedList<Map<String, String>>();

        for (WebElement record : records) {
            List<WebElement> fields = record.findElements(By.tagName("td"));
            Map<String, String> row = new HashMap<String, String>();

            for (int i = 0; i < fields.size(); i++) {
                moveToElement(fields.get(i));
                row.put(headNames.get(i), fields.get(i).getText());
            }

            result.add(row);
        }
        
        Iterator<Map<String,String>> iterator = result.iterator();
        
        while (iterator.hasNext()) {
            Map<String,String> row = iterator.next();
            
            if (!row.get("Payload").contains(String.format("\"%s\"", userId))) {
                iterator.remove();
            } else if (!row.get("Payload").contains(String.format("\"%s\"", eventName))) {
                iterator.remove();
            }
        }
        
        return result;
    }
    
    public Map<String,String> findLatestEventByEmail(String email, String eventName) throws Exception {
        return findLatestEventByPid(emailToPid(email), eventName);
    }
    
    public Map<String,String> findLatestEventByPid(String pid, String eventName) throws Exception {
        List<Map<String,String>> events = findValidEventByPid(pid, eventName);
        
        if (events.isEmpty()) {
            events = findInvalidEventByPid(pid, eventName);
            
            if (events.isEmpty()) {
                Assert.fail("event not found");
            } else {
                Assert.fail("event is invalid: " + objectMapper.writeValueAsString(events.get(0)));
            }
        } else {
            return events.get(0);
        }

        return null; // not reachable
    }
}
